package trading.algo.BearTask;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import trading.algo.STKShortSellingTask;
import trading.algo.TimerKillerTask;
import trading.bo.DBAccessorBO;
import trading.bo.EmailAccessorBO;
import trading.bo.IBAccessorBO;
import trading.dataentry.MonitoredStocks;
import trading.dataentry.OptionPositionOrOrder;
import trading.util.OrderSplitHelper;

/**
 * Add new short positions when market condition is met. Add option selling
 * orders if we have option open position but no closing order.
 * 
 * @author ntantai
 * 
 */
public class OrderCreationTask extends TimerTask {

	private DBAccessorBO dbAccessorBO;
	private IBAccessorBO ibAccessorBO;
	private EmailAccessorBO emailAccessorBO;
	private OrderSplitHelper orderSplitHelper;
	// Maps VXX up percentage to new position percentage.
	private List<Integer> vxxUpPercentage;
	private List<Double> vxxNewOrderPercentage;

	public OrderCreationTask(DBAccessorBO dbAccessorBO,
			IBAccessorBO ibAccessorBO, EmailAccessorBO emailAccessorBO, OrderSplitHelper orderSplitHelper) {
		this.dbAccessorBO = dbAccessorBO;
		this.ibAccessorBO = ibAccessorBO;
		this.emailAccessorBO = emailAccessorBO;
		this.orderSplitHelper = orderSplitHelper;

		this.vxxUpPercentage = new ArrayList<Integer>();
		this.vxxUpPercentage.add(20);
		this.vxxUpPercentage.add(32);
		this.vxxUpPercentage.add(45);
		this.vxxUpPercentage.add(60);
		this.vxxUpPercentage.add(76);
		this.vxxUpPercentage.add(93);
		this.vxxUpPercentage.add(132);
		this.vxxUpPercentage.add(178);
		this.vxxUpPercentage.add(234);
		this.vxxUpPercentage.add(300);
		this.vxxUpPercentage.add(381);

		this.vxxNewOrderPercentage = new ArrayList<Double>();
		this.vxxNewOrderPercentage.add(0.5);
		this.vxxNewOrderPercentage.add(0.8);
		this.vxxNewOrderPercentage.add(1.2);
		this.vxxNewOrderPercentage.add(1.7);
		this.vxxNewOrderPercentage.add(2.3);
		this.vxxNewOrderPercentage.add(3.0);
		this.vxxNewOrderPercentage.add(2.3);
		this.vxxNewOrderPercentage.add(1.7);
		this.vxxNewOrderPercentage.add(1.2);
		this.vxxNewOrderPercentage.add(0.8);
		this.vxxNewOrderPercentage.add(0.5);
	}

	@Override
	public void run() {
		Timer timer = new Timer();
		int taskID = 0;
		// Step 1: create short selling order
		if (shouldCreateOrder()) {
			int numOfVxxSharesToShort = calcNumOfVxxSharesToShort();
			STKShortSellingTask task = new STKShortSellingTask(
					MonitoredStocks.TICKERS.VXX.name(), numOfVxxSharesToShort,
					this.ibAccessorBO, this.emailAccessorBO, this.orderSplitHelper, false);
			timer.schedule(task, taskID * 1000);
			taskID+=STKShortSellingTask.secondsNeeded();
		}
		// Step 2: check open option position
		List<OptionPositionOrOrder> optionPositions = this.dbAccessorBO
				.getPortfolioOptionPositions();
		// Step 3: check open option order
		List<OptionPositionOrOrder> optionOrders = this.dbAccessorBO
				.getOpenOptionOrders();

		// Step 4: create new option order or cancel order if necessary
        SyncOptionPositionAndOrderTask syncTask = new SyncOptionPositionAndOrderTask(optionOrders, optionPositions, ibAccessorBO, emailAccessorBO);
		timer.schedule(syncTask, taskID * 1000);
		taskID += SyncOptionPositionAndOrderTask.secondsNeeded();
        
		// Step 5: kill the timer
		TimerKillerTask task = new TimerKillerTask(timer);
		timer.schedule(task, taskID * 1000);

	}

	private boolean shouldCreateOrder() {
		// Step 1: check VIX future contango
		List<String> vixFutureContango = this.dbAccessorBO
				.getVIXFutureContango();
		boolean shouldCreateOrder = true;
		if (vixFutureContango.size() < 5) {
			shouldCreateOrder = false;
		} else {
			for (String contango : vixFutureContango) {
				if (Double.parseDouble(contango) < 0) {
					shouldCreateOrder = false;
					break;
				}
			}
		}
		// Step 2: check VIX spot price
		if (shouldCreateOrder) {
			Map<Integer, String> vixSpotPrices = this.dbAccessorBO
					.getVIXSpotPrices();
			if (vixSpotPrices.keySet().size() < 6) {
				shouldCreateOrder = false;
			} else {
				for (int i = 0; i < 5; i++) {
					double recentDayPrice = Double.parseDouble(vixSpotPrices
							.get(i));
					double lessRecentDayPrice = Double
							.parseDouble(vixSpotPrices.get(i + 1));
					if (recentDayPrice > lessRecentDayPrice) {
						shouldCreateOrder = false;
						break;
					}
				}
			}
		}
		return shouldCreateOrder;
	}

	private int calcNumOfVxxSharesToShort() {
		double netLiquidation = this.dbAccessorBO.getNetLiquidation();
		MonitoredStocks vxxInfo = this.dbAccessorBO
				.getMonitoredStockData(MonitoredStocks.TICKERS.VXX.name());
		double vxxLastPrice = vxxInfo.getLastPrice();
		double vxxHistoryLow = vxxInfo.getHistoricalLow();
		double percentage = this.getPercentageOfFundShouldBeInvested(
				vxxLastPrice, vxxHistoryLow);
		double moneyAllocated = netLiquidation * percentage / 100;
		if (moneyAllocated < 1000) {
			moneyAllocated = 1000;
		}
		return (int) (moneyAllocated / vxxLastPrice);
	}

	private double getPercentageOfFundShouldBeInvested(double vxxLastPrice,
			double vxxHistoricalLow) {
		int percentage = (int) (100 * (vxxLastPrice - vxxHistoricalLow) / vxxHistoricalLow);
		int index = this.getPercentageIndex(percentage);
		return this.vxxNewOrderPercentage.get(index);

	}

	private int getPercentageIndex(int percentage) {
		int index = -1;
		for (int i = 0; i < this.vxxUpPercentage.size(); i++) {
			if (this.vxxUpPercentage.get(i) > percentage) {
				index = i;
				break;
			}
		}
		if (index == -1) {
			index = this.vxxUpPercentage.size() + 1;
		} else if (index != 0) {
			int smallerPercentage = this.vxxUpPercentage.get(index - 1);
			int biggerPercentage = this.vxxUpPercentage.get(index);
			int smallerDifference = percentage - smallerPercentage;
			int biggerDifference = biggerPercentage - percentage;
			if (smallerDifference < biggerDifference) {
				index--;
			}
		}
		return index;
	}

	public static int secondsNeeded() {
	    return STKShortSellingTask.secondsNeeded() + SyncOptionPositionAndOrderTask.secondsNeeded() + 2;
	}
}
